using System.IO;
using System.Collections;
using System.Collections.Generic;
using System;
using Framework.Core;
using UnityEngine;
using UnityEngine.Networking;
/*
// 使用示例
//最新的资源版本号
var versionFromServer = "abbeab905ab0a5e498c37ef40d2e2b68";
//远程资源地址
var updateURLFromServer = "http://127.0.0.1/bundles/";
//是否开启更新模式
var updateEnable = false;
var versionFile = "AssetBundles/version.json";
var downloader = new BundleDownload(_core.Coroutine);
if(!updateEnable)
{
    updateURLFromServer=string.Empty;
}
yield return downloader.Initialize(updateURLFromServer, versionFile, versionFromServer);
if (!downloader.Enabled)
{
    yield break;
}
//请求更新资源
yield return downloader.Request();
if (!downloader.RequestFinished)
{
    FrameworkLog.LogError("网络请求失败");
    yield break;
}
bool downloadNow = true;
if (downloadNow)
{
    // 统计并下载文件
    var count = downloader.NeedUpdateFileCount;
    while (downloader.NeedUpdateFileCount > 0)
    {
        downloader.Download(downloader.ReadUpdateFile());
    }
    //已下载个数
    int loadedCount = 0;
    while (loadedCount < count)
    {
        var c = downloader.LoadSuccessFiles.Count;
        while (c-- > 0)
        {
            var file = downloader.LoadSuccessFiles.Dequeue();
            loadedCount++;
            FrameworkLog.LogErrorFormat("{0}/{1}", loadedCount, count);
        }
        yield return null;
    }
    if (downloader.LoadErrorFiles.Count > 0)
    {
        FrameworkLog.LogError("文件下载存在失败的条目");
        //重新执行一次当前方法
        yield break;
    }
}

*/
namespace Framework
{
    public class BundleDownload : EventDispatcher
    {

        public string StreamingAssetsPath { get; private set; }
        public readonly string BundleDownloadSuccess = Guid.NewGuid().ToString();
        private const string DownloadingExt = ".download";
        //有没有磁盘读写能力
        public bool FileSystemEnabled { get { return FileSystem != null; } }
        public IFileSystem FileSystem { get; private set; }
        //加载完成的文件 
        public BundleInfo LoadedFile { get; private set; }
        public int DownLoadMaxCount { get; private set; }
        // 是否检测完本地信息
        public bool Enabled { get; private set; }
        //是否有版本变化
        public bool VersionChanged { get; private set; }
        //是否完成了流程
        public bool RequestFinished { get; private set; }
        public AssetsInfo RemoteAssetsInfo { get; private set; }
        public AssetsInfo CurrentAssetsInfo { get; private set; }
        // 远程地址
        public string RemotePath { get; private set; }
        public int NeedUpdateFileCount
        {
            get
            {
                return _updateFiles.Count;
            }
        }
        //已经下载完的文件列表
        public Queue<BundleInfo> LoadSuccessFiles { get; private set; }
        public Queue<BundleInfo> LoadErrorFiles { get; private set; }
        private string _assetPath;
        // 版本文件地址
        private string _versionPath;

        private bool _requesting;
        private readonly ICoroutineManager _coroutineManager;
        private AssetsInfo _localAssetInfo;
        private DownloadHandlerBuffer _remoteAssetInfoDownloadHandler;
        //下载地址
        private string _downloadPathFormat;
        //本地存放资源地址
        private string _persistentDataPath;
        private string _persistentDataPathFormat;
        private readonly Dictionary<string, DownloadItem> _workingMap;
        private readonly Queue<DownloadItem> _downloadTask;
        private readonly LinkedList<DownloadItem> _workingLoadTaskList;
        //需要更新的文件列表
        private readonly Queue<BundleInfo> _updateFiles;
        private bool _localAssetInfoInPersistentDataPath;

        public BundleDownload(ICoroutineManager coroutineManager)
        {
            _workingMap = new Dictionary<string, DownloadItem>();
            _downloadTask = new Queue<DownloadItem>();
            _workingLoadTaskList = new LinkedList<DownloadItem>();
            _updateFiles = new Queue<BundleInfo>();
            LoadSuccessFiles = new Queue<BundleInfo>();
            LoadErrorFiles = new Queue<BundleInfo>();
            Enabled = false;
            VersionChanged = true;
            RequestFinished = false;
            DownLoadMaxCount = 10;
            _persistentDataPath = Application.persistentDataPath;
            StreamingAssetsPath = Application.streamingAssetsPath;

            if (coroutineManager == null)
            {
                FrameworkLog.LogError("ICoroutineManager 不能为空对象");
                return;
            }
            _coroutineManager = coroutineManager;
            FileSystem = new DefaultFileSystem();
        }
        public void SetFileSystem(IFileSystem value)
        {
            this.FileSystem = value;
        }
        public string GetPersistentDataPath()
        {
            return _persistentDataPath;
        }
        public void SetPersistentDataPath(string path)
        {
            _persistentDataPath = path;
        }
        public void SetDownloadMaxCount(int count)
        {
            DownLoadMaxCount = Mathf.Max(1, count);
        }
        public void SetDownloadSpeed(int sizePreSeconds)
        {
            DownloadItem.downloadSpeed = sizePreSeconds;
        }
        /// <summary>
        /// 初始化信息 方法只会读取persistentDataPath 和 streamingAssetsPath 里的信息
        /// </summary>
        /// <param name="remotePath">远程更新地址 如果为空则为包内资源</param>
        /// <param name="versionFileName">描述文件相对地址 例如:AssetBundles/version.json</param>
        /// <param name="version">版本号 如果和本地一致则不会再请求远程信息</param>
        /// <returns></returns>
        public IEnumerator Initialize(string remotePath, string versionFileName, string version)
        {
            this.Enabled = false;
            RemotePath = remotePath;
            _versionPath = versionFileName;
            _assetPath = Path.GetDirectoryName(versionFileName).Replace("\\", "/");
            if (string.IsNullOrEmpty(RemotePath))
            {
                //使用包内资源
                this.Enabled = true;
                VersionChanged = false;
                deleteCacheFiles(_assetPath);
                yield break;
            }
            VersionChanged = true;
            if (!FileSystemEnabled)
            {
                this.Enabled = true;
                yield break;
            }
            FrameworkLog.LogFormat("最新版本:{0}", version);
            var infoPath = Path.Combine(_persistentDataPath, _versionPath);
            FrameworkLog.Log(_persistentDataPath);
            if (FileSystem.Exists(infoPath))
            {
                //从 persistentDataPath 获取版本信息
                if (FileSystem.LoadFileEnable())
                {
                    var assetsInfoRequest = UnityWebRequest.Get("file://" + infoPath);
                    yield return assetsInfoRequest.SendWebRequest();
                    if (assetsInfoRequest.isDone && assetsInfoRequest.result == UnityWebRequest.Result.Success)
                    {
                        try
                        {
                            AssetsInfo info = AssetsInfo.Decode(assetsInfoRequest.downloadHandler.data);
                            if (info != null)
                            {
                                _localAssetInfo = info;
                                if (info.Version == version)
                                {
                                    _localAssetInfoInPersistentDataPath = true;
                                    VersionChanged = false;
                                }
                                FrameworkLog.LogFormat("缓存版本:{0}", info.Version);
                            }
                        }
                        catch (System.Exception e)
                        {
                            FrameworkLog.LogError(e);
                            yield break;
                        }
                    }
                    assetsInfoRequest.Dispose();
                }
                else
                {
                    try
                    {
                        AssetsInfo info = AssetsInfo.Decode(FileSystem.ReadFile(infoPath));
                        if (info != null)
                        {
                            _localAssetInfo = info;
                            if (info.Version == version)
                            {
                                _localAssetInfoInPersistentDataPath = true;
                                VersionChanged = false;
                            }
                            FrameworkLog.LogFormat("缓存版本:{0}", info.Version);
                        }
                    }
                    catch (System.Exception e)
                    {
                        FrameworkLog.LogError(e);
                    }
                }
            }
            if (_localAssetInfo == null)
            {
                //persistentDataPath 里读不到再从 streamingAssetsPath 读
                if (string.IsNullOrEmpty(version) || VersionChanged)
                {
                    //从 streamingAssetsPath 获取版本信息
                    infoPath = Path.Combine(StreamingAssetsPath, _versionPath);
#if UNITY_WEBGL && !UNITY_EDITOR
                    infoPath += "?" + UnityEngine.Random.Range(100000, 9999999) + "_" + (DateTime.Now.Ticks);
#endif
                    var assetsInfoRequest = UnityWebRequest.Get((new System.Uri(infoPath)).AbsoluteUri);
                    yield return assetsInfoRequest.SendWebRequest();
                    if (assetsInfoRequest.isDone && assetsInfoRequest.result == UnityWebRequest.Result.Success)
                    {
                        try
                        {
                            AssetsInfo info = AssetsInfo.Decode(assetsInfoRequest.downloadHandler.data);
                            if (info != null)
                            {
                                _localAssetInfo = info;
                                if (info.Version == version)
                                {
                                    _localAssetInfoInPersistentDataPath = false;
                                    VersionChanged = false;
                                }
                                FrameworkLog.LogErrorFormat("包内版本:{0}", info.Version);
                            }
                        }
                        catch (System.Exception e)
                        {
                            FrameworkLog.LogError(e);
                            yield break;
                        }
                    }
                    assetsInfoRequest.Dispose();
                }
            }

            if (_localAssetInfo != null)
            {
                RemoteAssetsInfo = _localAssetInfo;
                CurrentAssetsInfo = _localAssetInfo;
                _persistentDataPathFormat = Path.Combine(_persistentDataPath, _assetPath, "{0}");
                _downloadPathFormat = Path.Combine(RemotePath, _assetPath, "{0}");
            }
            if (string.IsNullOrEmpty(version))
            {
                VersionChanged = true;
            }
            this.Enabled = true;
        }
        /// <summary>
        /// 如果版本信息变动则会请求更新，网络失败情况下重新请求
        /// </summary>
        /// <returns></returns>
        public IEnumerator Request()
        {
            _updateFiles.Clear();
            LoadSuccessFiles.Clear();
            LoadErrorFiles.Clear();
            if (_requesting)
            {
                FrameworkLog.LogError("已经开始请求远程信息,请稍等");
                yield break;
            }

            var assetPath = Path.Combine(_persistentDataPath, _assetPath);
            if (FileSystemEnabled)
            {
                if (!VersionChanged)
                {
                    if (_localAssetInfoInPersistentDataPath && _localAssetInfo != null)
                    {
                        getUpdateFiles(_localAssetInfo);
                    }
                    RequestFinished = true;
                    yield break;
                }
            }

            RequestFinished = false;
            _requesting = true;

            //请求远程地址
            AssetsInfo remoteAssetInfo = null;
            var infoPath = Path.Combine(RemotePath, _versionPath);
            if (infoPath.ToLower().StartsWith("http:"))
            {
#if UNITY_EDITOR
                FrameworkLog.LogErrorFormat("请使用https请求 {0}", infoPath);
#if UNITY_2022_1_OR_NEWER
                FrameworkLog.LogError("编辑器测试可修改 Other Settings->Configuration->Allow downloads over HTTP");
#endif
#endif
            }
#if UNITY_WEBGL && !UNITY_EDITOR
            infoPath += "?" + UnityEngine.Random.Range(100000, 9999999) + "_" + (DateTime.Now.Ticks);
#endif
            var assetsInfoRequest = UnityWebRequest.Get(infoPath);
            var downloadHandler = new DownloadHandlerBuffer();
            //远程描述文件数据，这个要在最后步骤保存到本地
            _remoteAssetInfoDownloadHandler = downloadHandler;
            assetsInfoRequest.downloadHandler = downloadHandler;
            assetsInfoRequest.disposeDownloadHandlerOnDispose = false;
            yield return assetsInfoRequest.SendWebRequest();
            if (assetsInfoRequest.isDone && assetsInfoRequest.result == UnityWebRequest.Result.Success)
            {
                try
                {
                    AssetsInfo info = AssetsInfo.Decode(downloadHandler.data);
                    remoteAssetInfo = info;
                    CurrentAssetsInfo = remoteAssetInfo;
                    RemoteAssetsInfo = remoteAssetInfo;
                    FrameworkLog.LogFormat("远程版本:{0}", info.Version);
                    if (!FileSystemEnabled)
                    {
                        _localAssetInfo = remoteAssetInfo;
                    }

                }
                catch (System.Exception e)
                {
                    FrameworkLog.LogError(e);
                    _requesting = false;
                    yield break;
                }
            }
            else
            {
                FrameworkLog.LogErrorFormat("远程版本不存在 {0}", RemotePath);
            }
            assetsInfoRequest.Dispose();
            if (!FileSystemEnabled)
            {
                RequestFinished = true;
                _requesting = false;
                yield break;
            }
            if (remoteAssetInfo != null)
            {
                _persistentDataPathFormat = Path.Combine(_persistentDataPath, _assetPath, "{0}");
                _downloadPathFormat = Path.Combine(RemotePath, _assetPath, "{0}");
                //比较本地文件是否要更新
                Dictionary<string, BundleInfo> localFiles = new Dictionary<string, BundleInfo>();
                Dictionary<string, BundleInfo> remoteFiles = new Dictionary<string, BundleInfo>();
                if (_localAssetInfo != null)
                {
                    foreach (var item in _localAssetInfo.Bundles)
                    {
                        localFiles.Add(item.Path, item);
                    }
                }
                _updateFiles.Clear();

                foreach (var item in remoteAssetInfo.Bundles)
                {
                    remoteFiles.Add(item.Path, item);
                    if (localFiles.TryGetValue(item.Path, out BundleInfo fileinfo))
                    {
                        if (!item.Equals(fileinfo))
                        {
                            //版本有变化
                            _updateFiles.Enqueue(item);
                            // FrameworkLog.LogError("变化" + item.Name);
                        }
                    }
                    else
                    {
                        //新增
                        _updateFiles.Enqueue(item);
                        // FrameworkLog.LogError("新增" + item.Name);
                    }
                }

                if (_localAssetInfo != null)
                {
                    List<BundleInfo> deleteFiles = new List<BundleInfo>();
                    foreach (var item in _localAssetInfo.Bundles)
                    {
                        if (!remoteFiles.ContainsKey(item.Path))
                        {
                            //本地有，远程没有则要删除
                            deleteFiles.Add(item);
                        }
                    }

                    foreach (var item in deleteFiles)
                    {
                        var filePath = Path.Combine(assetPath, item.Path);
                        if (FileSystem.Exists(filePath))
                        {
                            try
                            {
                                FileSystem.Delete(filePath);
                            }
                            catch (System.Exception)
                            {

                                FrameworkLog.LogErrorFormat("不需要的文件 删除失败 {0}", item.Path);
                            }
                        }
                    }
                }
                if (_updateFiles.Count > 0)
                {
                    FrameworkLog.LogFormat("更新文件数量:{0}", _updateFiles.Count);
                }
                foreach (var item in _updateFiles)
                {
                    var filePath = Path.Combine(assetPath, item.Path);
                    var dirInfo = Path.GetDirectoryName(filePath);
                    if (!FileSystem.Exists(dirInfo))
                    {
                        FileSystem.CreateDirectory(dirInfo);
                    }
                    var downloadFilePath = filePath + DownloadingExt;
                    if (!FileSystem.Exists(downloadFilePath))
                    {
                        FileSystem.WriteAllText(downloadFilePath, "");
                    }
                    if (FileSystem.Exists(filePath))
                    {
                        try
                        {
                            FileSystem.Delete(filePath);

                        }
                        catch (System.Exception)
                        {
                            FrameworkLog.LogErrorFormat("需要更新的文件 删除失败 {0}", item.Path);
                        }
                    }
                }
                RemoteAssetsInfo = remoteAssetInfo;
                _localAssetInfo = remoteAssetInfo;
                Utils.Unity.WriteAllBytes(Path.Combine(_persistentDataPath, _versionPath), _remoteAssetInfoDownloadHandler);
                _localAssetInfoInPersistentDataPath = true;
                getUpdateFiles(remoteAssetInfo);

            }
            assetsInfoRequest.Dispose();
            _requesting = false;
            RequestFinished = true;
        }
        private void getUpdateFiles(AssetsInfo info)
        {
            _updateFiles.Clear();
            var assetPath = Path.Combine(_persistentDataPath, _assetPath);
            foreach (var item in _localAssetInfo.Bundles)
            {
                var filePath = Path.Combine(assetPath, item.Path);
                var downloadFilePath = filePath + DownloadingExt;
                if (FileSystem.Exists(downloadFilePath))
                {
                    _updateFiles.Enqueue(item);
                }
            }
        }
        private void deleteCacheFiles(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                return;
            }
            var dir = Path.Combine(_persistentDataPath, path);
            if (!FileSystem.Exists(dir)) return;
            var fileList = FileSystem.GetFileSystemEntries(dir);
            foreach (var item in fileList)
            {
                try
                {
                    if (FileSystem.Exists(item))
                    {
                        FileSystem.Delete(item);
                        continue;
                    }
                }
                catch (System.Exception)
                {
                    FrameworkLog.LogErrorFormat("删除文件失败 {0}", item);
                }
            }
        }
        public void Download(BundleInfo bundleAsset)
        {
            if (bundleAsset == null)
            {
                FrameworkLog.LogError("参数不能为空");
                return;
            }
            if (!Enabled || _requesting)
            {
                FrameworkLog.LogError("下载器还没有准备好");
                return;
            }
            if (RemoteAssetsInfo == null && _localAssetInfo == null)
            {
                FrameworkLog.LogError("没有找到资源信息");
                return;
            }
            if (_workingMap.ContainsKey(bundleAsset.Path))
            {
                return;
            }
            // FrameworkLog.LogErrorFormat("开始下载{0}", bundleAsset.Name);

            var item = new DownloadItem(bundleAsset, _persistentDataPathFormat, loadFinish);
            item.FileSystem = this.FileSystem;
            _workingMap.Add(bundleAsset.Path, item);

            if (_workingLoadTaskList.Count < DownLoadMaxCount)
            {
                _workingLoadTaskList.AddLast(item);
                var downloadFilePath = string.Format(_downloadPathFormat, item.Info.Path);
                _coroutineManager.Start(item.Begin(downloadFilePath), "download " + item.Info.Path);
            }
            else
            {
                _downloadTask.Enqueue(item);
            }
            return;
        }
        public BundleInfo ReadUpdateFile()
        {
            if (_updateFiles.Count > 0)
            {
                return _updateFiles.Dequeue();
            }
            return null;
        }

        private void loadFinish(DownloadItem item)
        {
            bool success = true;

            var filePath = string.Format(_persistentDataPathFormat, item.Info.Path);
            try
            {
                if (item.DataLength == item.Info.Size)
                {
                    var downloadFilePath = filePath + DownloadingExt;
                    if (FileSystem.Exists(downloadFilePath))
                    {
                        FileSystem.Delete(downloadFilePath);
                    }
                }
                else
                {
                    success = false;
                    if (FileSystem.Exists(filePath))
                    {
                        FileSystem.Delete(filePath);
                    }
                    FrameworkLog.LogErrorFormat("{0}文件大小错误 应为 {1} 下载文件大小为 {2}", item.Info.Path, item.Info.Size, item.DataLength);
                }

            }
            catch (System.Exception)
            {
                success = false;
                FrameworkLog.LogErrorFormat("保存文件失败{0}", item.Info.Path);
            }
            if (success)
            {
                LoadSuccessFiles.Enqueue(item.Info);
                LoadedFile = item.Info;
                this.DispatchEvent(this.BundleDownloadSuccess);
            }
            else
            {
                LoadErrorFiles.Enqueue(item.Info);
            }
            _workingLoadTaskList.Remove(item);
            if (_downloadTask.Count < 1)
            {
                return;
            }
            if (_workingLoadTaskList.Count < DownLoadMaxCount)
            {
                item = _downloadTask.Dequeue();
                _workingLoadTaskList.AddLast(item);
                filePath = string.Format(_downloadPathFormat, item.Info.Path);
                _coroutineManager.Start(item.Begin(filePath), "download " + item.Info.Path);
            }
        }
        internal class DownloadItem
        {
            public static int downloadSpeed;
            private static byte[] _downloadBuffer;
            private static byte[] _tempBuffer;
            public IFileSystem FileSystem;
            public int RetryCount;
            public int DataLength { get; private set; }
            public BundleInfo Info { get; private set; }
            private Action<DownloadItem> _finishHandler;
            private string _filePath;
            private float _simDelayTime;
            public DownloadItem(BundleInfo info, string persistentDataPathFormat, Action<DownloadItem> finishHandler)
            {
                Info = info;
                _finishHandler = finishHandler;
                RetryCount = 3;
                _filePath = string.Format(persistentDataPathFormat, Info.Path);

            }
            public IEnumerator Begin(string path)
            {
                while (RetryCount-- > 0)
                {
                    var dirInfo = Path.GetDirectoryName(_filePath);
                    if (!FileSystem.Exists(dirInfo))
                    {
                        FileSystem.CreateDirectory(dirInfo);
                    }
                    if (FileSystem.Exists(_filePath))
                    {
                        FileSystem.Delete(_filePath);
                    }
                    var downloadRequest = UnityWebRequest.Get(path);
                    FileStreamHandler streamHandler = null;
                    DownloadFileBufferHandler fileHandler = null;
                    if (FileSystem.LoadFileEnable())
                    {
                        if (_downloadBuffer == null)
                        {
                            _downloadBuffer = new byte[1024 * 100];
                        }
                        streamHandler = new FileStreamHandler(_filePath, _downloadBuffer);
                        downloadRequest.downloadHandler = streamHandler;
                    }
                    else
                    {
                        if (_downloadBuffer == null)
                        {
                            _downloadBuffer = new byte[1024];
                        }
                        fileHandler = new DownloadFileBufferHandler(_downloadBuffer);
                        fileHandler.SetBuffer(new byte[Info.Size]);
                        downloadRequest.downloadHandler = fileHandler;
                    }
                    downloadRequest.disposeUploadHandlerOnDispose = true;
                    yield return downloadRequest.SendWebRequest();
#if UNITY_EDITOR
                    if (downloadSpeed > 0)
                    {
                        //模拟下载速度
                        float delayTime = (float)Info.Size / (float)downloadSpeed;
                        Debug.LogWarning("模拟下载时间" + delayTime);
                        yield return new WaitForSeconds(delayTime);
                    }
#endif
                    if (downloadRequest.isDone && downloadRequest.result == UnityWebRequest.Result.Success)
                    {
                        if (FileSystem.LoadFileEnable())
                        {
                            DataLength = streamHandler.Length;
                        }
                        else
                        {
                            DataLength = fileHandler.Length;
                            FileSystem.WriteAllBytes(_filePath, fileHandler.Buffer, 0, fileHandler.Length);
                        }

                        if (_finishHandler != null)
                        {
                            _finishHandler.Invoke(this);
                        }
                        downloadRequest.Dispose();
                        yield break;
                    }
                    downloadRequest.Dispose();
                }


            }
        }
    }
}